package alibaba;


import java.io.File;
import java.util.Set;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;

public class AlibabaClient {

	enum Action {
		BACKUP_DIR, 
		BACKUP_FILE,
		RESTORE_DIR, 
		RESTORE_FILE, 
		HELP,
		ERROR, 
		LIST,
		CHECK, 
		CREATE_SNAPSHOT, 
		DELETE_SNAPSHOT
	}

	/**
	 * Print help menu
	 */
	protected static  void printMainHelp() {
		System.out.println("Usage: alibaba command [options]");	        
		System.out.println("Commands:\n");
		System.out.println("Command name\t\tShort name\tDescription");
		System.out.println("--------------------------------------------------------------");
		System.out.println("backupdir\t\tbd\t\tBackup a directory");
		System.out.println("backupfile\t\tbf\t\tBackup a file");
		System.out.println("restoredir\t\trd\t\tRestore a directory");
		System.out.println("restorefile\t\tls\t\tList a directory");
		System.out.println("checkall\t\tch\t\tCheck and validate backup files");
		System.out.println("help\t\t\t?\t\tPrint usage information");
		System.out.println("createsnapshot\t\tcs\t\tCreate a snapshot for the user files");
		System.out.println("deletesnapshot\t\t\tds\t\tDelete a specified user files snapshot");		
	}

	/**
	 * Backup directory into server
	 * 
	 * @param args - required format: -user <user_name> -dir <dir_name>
	 */
	protected static int backupDirCmd(String[] args) throws Exception {
		CommandLineParser parser = new GnuParser();
		Options commandOptions = new Options();
		CommandLine commandLine;
		HelpFormatter formatter = new HelpFormatter();

		OptionBuilder.withDescription("The user name");
		OptionBuilder.hasArg();
		OptionBuilder.withArgName("user_name");
		OptionBuilder.isRequired();
		commandOptions.addOption(OptionBuilder.create("user"));

		OptionBuilder.withDescription("The name of the directory");
		OptionBuilder.hasArg();
		OptionBuilder.withArgName("directory_name");
		OptionBuilder.isRequired();
		commandOptions.addOption(OptionBuilder.create("dir"));

		try {
			commandLine = parser.parse(commandOptions, args);
		} catch (ParseException exp) {
			System.out.println(exp.getMessage());
			formatter.printHelp("backupdir", commandOptions);
			return -1;
		}
		Service.setUser(commandLine.getOptionValue("user"));
		Service.setSnapshot(-1);
		String directoryName = commandLine.getOptionValue("dir");

		// Add file separator if missing
		if (!directoryName.endsWith(File.separator))
			directoryName += File.separator;
		System.out.println("Directory: " + directoryName);
		Service.backupDir(directoryName);
		return 1; 
	}

	/**
	 * Restore directory from server
	 * 
	 * @param args - required format: -user <user_name> -dir <dir_name>
	 *             - optional format: -snapshot <snapshot_number>
	 */
	protected static int restoreDirCmd(String[] args) throws Exception {
		CommandLineParser parser = new GnuParser();
		Options commandOptions = new Options();
		CommandLine commandLine;
		HelpFormatter formatter = new HelpFormatter();

		OptionBuilder.withDescription("The user name");
		OptionBuilder.hasArg();
		OptionBuilder.withArgName("user_name");
		OptionBuilder.isRequired();
		commandOptions.addOption(OptionBuilder.create("user"));

		OptionBuilder.withDescription("The name of the directory");
		OptionBuilder.hasArg();
		OptionBuilder.withArgName("directory_name");
		OptionBuilder.isRequired();
		commandOptions.addOption(OptionBuilder.create("dir"));

		OptionBuilder.withDescription("Snapshot number");
		OptionBuilder.hasArg();
		OptionBuilder.withArgName("snapshot_number");
		OptionBuilder.isRequired(false);
		commandOptions.addOption(OptionBuilder.create("snapshot"));

		try {
			commandLine = parser.parse(commandOptions, args);
		} catch (ParseException exp) {
			System.out.println(exp.getMessage());
			formatter.printHelp("restoredir", commandOptions);
			return -1;
		}
		Service.setUser(commandLine.getOptionValue("user"));
		String directoryName = commandLine.getOptionValue("dir");

		getSnapshot(commandLine);

		// Add file separator if missing
		if (!directoryName.endsWith(File.separator))
			directoryName += File.separator;

		System.out.println("Directory: " + directoryName);
		Service.restoreDir(directoryName);
		return 1; 
	}

	/**
	 * Backup file into server
	 * 
	 * @param args - required format: -user <user_name> -file <file_name>
	 */
	protected static int backupFileCmd(String[] args) throws Exception {
		CommandLineParser parser = new GnuParser();
		Options commandOptions = new Options();
		CommandLine commandLine;
		HelpFormatter formatter = new HelpFormatter();

		OptionBuilder.withDescription("The user name");
		OptionBuilder.hasArg();
		OptionBuilder.withArgName("user_name");
		OptionBuilder.isRequired();
		commandOptions.addOption(OptionBuilder.create("user"));

		OptionBuilder.withDescription("The name of the file");
		OptionBuilder.hasArg();
		OptionBuilder.withArgName("file_name");
		OptionBuilder.isRequired();
		commandOptions.addOption(OptionBuilder.create("file"));

		try {
			commandLine = parser.parse(commandOptions, args);
		} catch (ParseException exp) {
			System.out.println(exp.getMessage());
			formatter.printHelp("backupfile", commandOptions);
			return -1;
		}
		Service.setUser(commandLine.getOptionValue("user"));
		Service.setSnapshot(-1);
		String fileName = commandLine.getOptionValue("file");

		System.out.println("File: " + fileName);
		Service.backupFile(fileName);
		return 1; 
	}

	/**
	 * Restore file from server
	 * 
	 * @param args - required format: -user <user_name> -file <file_name>
	 *             - optional format: -snapshot <snapshot_number>
	 */
	protected static int restoreFileCmd(String[] args) throws Exception {
		CommandLineParser parser = new GnuParser();
		Options commandOptions = new Options();
		CommandLine commandLine;
		HelpFormatter formatter = new HelpFormatter();

		OptionBuilder.withDescription("The user name");
		OptionBuilder.hasArg();
		OptionBuilder.withArgName("user_name");
		OptionBuilder.isRequired();
		commandOptions.addOption(OptionBuilder.create("user"));

		OptionBuilder.withDescription("The name of the file");
		OptionBuilder.hasArg();
		OptionBuilder.withArgName("file_name");
		OptionBuilder.isRequired();
		commandOptions.addOption(OptionBuilder.create("file"));

		OptionBuilder.withDescription("Snapshot number");
		OptionBuilder.hasArg();
		OptionBuilder.withArgName("snapshot_number");
		OptionBuilder.isRequired(false);
		commandOptions.addOption(OptionBuilder.create("snapshot"));

		try {
			commandLine = parser.parse(commandOptions, args);
		} catch (ParseException exp) {
			System.out.println(exp.getMessage());
			formatter.printHelp("restorefile", commandOptions);
			return -1;
		}
		Service.setUser(commandLine.getOptionValue("user"));
		String fileName = commandLine.getOptionValue("file");
		getSnapshot(commandLine);
		
		System.out.println("File: " + fileName);
		Service.restoreFile(fileName);
		return 1; 
	}

	/**
	 * list directory files
	 * 
	 * @param args - required format: -user <user_name> -dir <dir_name>
	 *             - optional format: -snapshot <snapshot_number> 
	 */
	protected static int listCmd(String[] args) throws Exception {
		CommandLineParser parser = new GnuParser();
		Options commandOptions = new Options();
		CommandLine commandLine;
		HelpFormatter formatter = new HelpFormatter();
		OptionBuilder.withDescription("The user name");
		OptionBuilder.hasArg();
		OptionBuilder.withArgName("user_name");
		OptionBuilder.isRequired();
		commandOptions.addOption(OptionBuilder.create("user"));

		OptionBuilder.withDescription("The name of the directory");
		OptionBuilder.hasArg();
		OptionBuilder.withArgName("directory_name");
		OptionBuilder.isRequired();
		commandOptions.addOption(OptionBuilder.create("dir"));

		OptionBuilder.withDescription("Snapshot number");
		OptionBuilder.hasArg();
		OptionBuilder.withArgName("snapshot_number");
		OptionBuilder.isRequired(false);
		commandOptions.addOption(OptionBuilder.create("snapshot"));

		try {
			commandLine = parser.parse(commandOptions, args);
		} catch (ParseException exp) {
			System.out.println(exp.getMessage());
			formatter.printHelp("list", commandOptions);
			return -1;
		}
		Service.setUser(commandLine.getOptionValue("user"));
		String directoryName = commandLine.getOptionValue("dir");

		// Add file separator if missing
		if (!directoryName.endsWith(File.separator))
			directoryName += File.separator;

		getSnapshot(commandLine);
		
		System.out.println("Directory of " + directoryName);
		Set<String> files = Service.list(directoryName);

		if (files == null) {
			System.out.println("No files are stored for directory " + directoryName);
			return 1;
		}

		for (String file : files) {
			System.out.println("\t " + file);
		}
		return 1; 
	}

	/**
	 * Check directory on server
	 * 
	 * @param args - required format: -user <user_name> -dir <dir_name>
	 *             - optional format: -snapshot <snapshot_number> 
	 */
	protected static int checkCmd(String[] args) throws Exception {
		CommandLineParser parser = new GnuParser();
		Options commandOptions = new Options();
		CommandLine commandLine;
		HelpFormatter formatter = new HelpFormatter();

		OptionBuilder.withDescription("The user name");
		OptionBuilder.hasArg();
		OptionBuilder.withArgName("user_name");
		OptionBuilder.isRequired();
		commandOptions.addOption(OptionBuilder.create("user"));

		OptionBuilder.withDescription("The name of the directory");
		OptionBuilder.hasArg();
		OptionBuilder.withArgName("directory_name");
		OptionBuilder.isRequired(false);
		commandOptions.addOption(OptionBuilder.create("dir"));

		try {
			commandLine = parser.parse(commandOptions, args);
		} catch (ParseException exp) {
			System.out.println(exp.getMessage());
			formatter.printHelp("check", commandOptions);
			return -1;
		}
		Service.setUser(commandLine.getOptionValue("user"));
		String directoryName = commandLine.getOptionValue("dir");

		// Add file separator if missing		
		if (!directoryName.endsWith(File.separator))
			directoryName += File.separator;

		System.out.println("Directory: " + directoryName);
		Service.check(directoryName);

		return 1; 
	}

	/**
	 * Check directory on server
	 * 
	 * @param args - required format: -user <user_name>
	 *             - optional format: -snapshot <snapshot_number> 
	 */
	protected static int createSnapshotCmd(String[] args) throws Exception {
		CommandLineParser parser = new GnuParser();
		Options commandOptions = new Options();
		CommandLine commandLine;
		HelpFormatter formatter = new HelpFormatter();

		OptionBuilder.withDescription("The user name");
		OptionBuilder.hasArg();
		OptionBuilder.withArgName("user_name");
		OptionBuilder.isRequired();
		commandOptions.addOption(OptionBuilder.create("user"));

		OptionBuilder.withDescription("Snapshot number");
		OptionBuilder.hasArg();
		OptionBuilder.withArgName("snapshot_number");
		OptionBuilder.isRequired();
		commandOptions.addOption(OptionBuilder.create("snapshot"));

		try {
			commandLine = parser.parse(commandOptions, args);
		} catch (ParseException exp) {
			System.out.println(exp.getMessage());
			formatter.printHelp("createsnapshot", commandOptions);
			return -1;
		}
		Service.setUser(commandLine.getOptionValue("user"));

		int snapshot = getSnapshot(commandLine);
		
		System.out.println("Creating snapshot " + snapshot);
		Service.createSnapshot();

		return 1; 
	}

	/**
	 * Check directory on server
	 * 
	 * @param args - required format: -user <user_name>
	 *             - optional format: -snapshot <snapshot_number> 
	 */
	protected static int deleteSnapshotCmd(String[] args) throws Exception {
		CommandLineParser parser = new GnuParser();
		Options commandOptions = new Options();
		CommandLine commandLine;
		HelpFormatter formatter = new HelpFormatter();

		OptionBuilder.withDescription("The user name");
		OptionBuilder.hasArg();
		OptionBuilder.withArgName("user_name");
		OptionBuilder.isRequired();
		commandOptions.addOption(OptionBuilder.create("user"));

		OptionBuilder.withDescription("Snapshot number");
		OptionBuilder.hasArg();
		OptionBuilder.withArgName("snapshot_number");
		OptionBuilder.isRequired();
		commandOptions.addOption(OptionBuilder.create("snapshot"));

		try {
			commandLine = parser.parse(commandOptions, args);
		} catch (ParseException exp) {
			System.out.println(exp.getMessage());
			formatter.printHelp("deletesnapshot", commandOptions);
			return -1;
		}
		Service.setUser(commandLine.getOptionValue("user"));

		int snapshot = getSnapshot(commandLine);
		
		System.out.println("Deleting snapshot " + snapshot);
		Service.deleteSnapshot();

		return 1; 
	}

	/**
	 * Parse command 
	 * 
	 * @param args - command line arguments
	 */
	protected static Action parseAction(String[] args){
		String commandString = args[0];
		if (commandString.equals("backupfile")
				|| commandString.equals("bf")) {
			return Action.BACKUP_FILE;
		} else if (commandString.equals("backupdir")
				|| commandString.equals("bd")) {
			return Action.BACKUP_DIR;
		} else if (commandString.equals("restorefile")
				|| commandString.equals("rf")) {
			return Action.RESTORE_FILE;
		} else if (commandString.equals("restoredir")
				|| commandString.equals("rd")) {
			return Action.RESTORE_DIR;
		} else if (commandString.equals("list")
				|| commandString.equals("ls")) {
			return Action.LIST;
		} else if (commandString.equals("check")
				|| commandString.equals("ch")) {
			return Action.CHECK;
		} else if (commandString.equals("createsnapshot")
				|| commandString.equals("cs")) {
			return Action.CREATE_SNAPSHOT;
		} else if (commandString.equals("deletesnapshot")
				|| commandString.equals("ds")) {
			return Action.DELETE_SNAPSHOT;						
		} else if (commandString.equals("help") || commandString.equals("?")) {
			return Action.HELP;
		}
		else {
			return Action.ERROR;
		}
	}

	/**
	 * Execute command 
	 * 
	 * @param args - command line arguments
	 * @throws Exception 
	 */
	private static void doCommand(String[] args) {

		Action action = parseAction(args);
		if (action == Action.ERROR) {
			System.out.println("Input error. Type 'help' for usage");
			return;
		}
		String[] cmdArgs = new String[args.length - 1];
		System.arraycopy(args, 1, cmdArgs, 0, args.length - 1);
		Service.setNewLine();
		try {

			switch (action) {

			case BACKUP_FILE:
				System.out.println("Backup file command");
				backupFileCmd(cmdArgs);				
				break;

			case BACKUP_DIR:
				System.out.println("Backup dir command");
				backupDirCmd(cmdArgs);
				break;

			case RESTORE_FILE:
				System.out.println("Restore file command");
				restoreFileCmd(cmdArgs);
				break;

			case RESTORE_DIR:
				System.out.println("Restore dir command");
				restoreDirCmd(cmdArgs);
				break;

			case LIST:
				System.out.println("List command");
				listCmd(cmdArgs);
				break;

			case CHECK:
				System.out.println("Check command");
				checkCmd(cmdArgs);
				break;

			case HELP:
				printMainHelp();
				break;
			case CREATE_SNAPSHOT:
				createSnapshotCmd(cmdArgs);
				break;
			case DELETE_SNAPSHOT:
				deleteSnapshotCmd(cmdArgs);
				break;

			default:
				System.out.println("Not implememented yet.");
			}
		} catch (Exception e) {
		}
	}

	/**
	 * @param args
	 * @throws Exception 
	 */
	public static void main(String[] args) {
		if (args.length == 0) {
			System.out.println("Type 'alibaba help' for usage");
			System.exit(-1);
		}
		doCommand(args);
		return; 
	}

	private static int getSnapshot(CommandLine commandLine) {
		int snapshot = -1;
		if (commandLine.hasOption("snapshot")) {
			snapshot = new Integer(commandLine.getOptionValue("snapshot"));
			if (snapshot <= 0) {
				System.out.println("Snapshot number must be positive");
				System.exit(1);
			}
		}
		Service.setSnapshot(snapshot);
		return snapshot;
	}

}

